package com.balance.anole.erp.security.login.view;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
import org.springframework.remoting.RemoteConnectFailureException;

import com.balance.anole.admin.to.LoginUserTO;
import com.balance.anole.admin.to.StaffInfoTO;
import com.balance.anole.erp.Activator;
import com.balance.anole.erp.security.SessionManager;
import com.balance.anole.erp.security.login.model.RetailLoginUser;
import com.balance.anole.erp.security.login.service.LoginContext;
import com.balance.anole.erp.security.login.service.LoginDelegator;
import com.balance.anole.erp.security.login.service.LoginType;
import com.balance.anole.erp.security.login.service.impl.LocalLoginHandler;
import com.balance.anole.erp.security.login.service.impl.RemoteLoginHandler;
import com.balance.anole.erp.util.Messages;
import com.balance.anole.erp.util.SWTResourceManager;

/**
 * Login dialog, which prompts for the user's account info, and has Login and
 * Cancel buttons.
 */
public class LoginDialog extends Dialog {

	private Combo userIdText;

	private ComboViewer loginType;

	private Text passwordText;

	private Button removeCurrentUser;

	private LoginContext loginContext;

	private Map<String, LoginContext> savedDetails = new TreeMap<String, LoginContext>();

	private Image[] images;

/*	private Button offlineButton;

	private Button autoLogin;*/

	private Label infoLabel;

	private static final String PASSWORD = "password"; //$NON-NLS-1$

	private static final String LOGIN_TYPE = "loginType"; //$NON-NLS-1$

	private static final String SAVED = "saved-login-preferences"; //$NON-NLS-1$

	private static final String LAST_USER = "prefs_last_connection"; //$NON-NLS-1$

	public LoginDialog(Shell parentShell) {
		super(parentShell);
		loadDescriptors();
	}

	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);
		newShell.setText(Messages.LOGIN_DIALOG_TITLE);
		// load the image from the product definition
		/*IProduct product = Platform.getProduct();
		if (product != null) {
			String[] imageURLs = parseCSL(product
					.getProperty(IProductConstants.WINDOW_IMAGES));
			if (imageURLs.length > 0) {
				images = new Image[imageURLs.length];
				for (int i = 0; i < imageURLs.length; i++) {
					String url = imageURLs[i];
					ImageDescriptor descriptor = AbstractUIPlugin
							.imageDescriptorFromPlugin(product
									.getDefiningBundle().getSymbolicName(), url);
					images[i] = descriptor.createImage(true);
				}
				newShell.setImages(images);
			}
		}*/
	}

	public static String[] parseCSL(String csl) {
		if (csl == null)
			return null;

		StringTokenizer tokens = new StringTokenizer(csl, ","); //$NON-NLS-1$
		List<String> array = new ArrayList<String>(10);
		while (tokens.hasMoreTokens())
			array.add(tokens.nextToken().trim());

		return (String[]) array.toArray(new String[array.size()]);
	}

	public boolean close() {
		if (images != null) {
			for (int i = 0; i < images.length; i++)
				images[i].dispose();
		}
		return super.close();
	}

	protected Control createDialogArea(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout(2, false);
		composite.setLayout(layout);

		Label accountLabel = new Label(composite, SWT.NONE);
		accountLabel.setText(Messages.LOGIN_DIALOG_ACCOUNT_INFO);
		accountLabel.setLayoutData(new GridData(SWT.LEFT,
				GridData.CENTER, false, false, 2, 1));

		/*infoLabel = new  Label(composite, SWT.NONE);
		infoLabel.setForeground(SWTResourceManager.getColor(0, 0, 255));
		infoLabel.setLayoutData(new GridData(SWT.LEFT,
				GridData.CENTER, true, true, 1, 1));*/

		Label userIdLabel = new Label(composite, SWT.NONE);
		userIdLabel.setText(Messages.LOGIN_DIALOG_USER_NAME);
		userIdLabel.setLayoutData(new GridData(SWT.LEFT, GridData.CENTER,
				false, false));

		userIdText = new Combo(composite, SWT.BORDER);
		GridData gridData = new GridData(GridData.FILL, GridData.FILL, true,
				false);
		gridData.widthHint = convertHeightInCharsToPixels(20);
		userIdText.setLayoutData(gridData);
		userIdText.addListener(SWT.Modify, new Listener() {
			public void handleEvent(Event event) {
				LoginContext d = (LoginContext) savedDetails
						.get(userIdText.getText());
				if (d != null) {
					loginType.setSelection(new StructuredSelection(d.getLoginType()));
					passwordText.setText(d.getUser().getPassword());
				}
			}
		});

		Label passwordLabel = new Label(composite, SWT.NONE);
		passwordLabel.setText(Messages.LOGIN_DIALOG_PASSWORD);
		passwordLabel.setLayoutData(new GridData(SWT.LEFT, GridData.CENTER,
				false, false));

		passwordText = new Text(composite, SWT.BORDER | SWT.PASSWORD);
		passwordText.setLayoutData(new GridData(GridData.FILL, GridData.FILL,
				true, false));

		/*autoLogin = new Button(composite, SWT.CHECK);
		autoLogin.setText(Messages.LOGIN_DIALOG_AUTO_LOGIN);
		autoLogin.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true,
				true, 2, 1));
		autoLogin.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				IEclipsePreferences prefs = new ConfigurationScope()
						.getNode(Application.PLUGIN_ID);
				prefs.putBoolean(GeneralPreferencePage.AUTO_LOGIN, autoLogin
						.getSelection());
			}
		});
		IPreferencesService service = Platform.getPreferencesService();
		boolean auto_login = service.getBoolean(Application.PLUGIN_ID,
				GeneralPreferencePage.AUTO_LOGIN, true, null);

		autoLogin.setSelection(auto_login);

		offlineButton = new Button(composite, SWT.CHECK);
		final GridData gd_offlineButton = new GridData();
		offlineButton.setLayoutData(gd_offlineButton);
		offlineButton.setText("Offline");

		boolean offline = service.getBoolean(Application.PLUGIN_ID,
				GeneralPreferencePage.OFFLINE, true, null);

		offlineButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				IEclipsePreferences prefs = new ConfigurationScope()
				.getNode(Application.PLUGIN_ID);
				prefs.putBoolean(GeneralPreferencePage.OFFLINE, offlineButton
						.getSelection());
			}
		});

		offlineButton.setSelection(offline);*/

		Label loginTypeLabel = new Label(composite, SWT.NONE);
		loginTypeLabel.setText(Messages.LOGIN_DIALOG_TYPE);
		loginTypeLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER,
				false, false));

		loginType = new ComboViewer(composite, SWT.READ_ONLY);
		loginType.getCombo().setLayoutData(new GridData(SWT.FILL, SWT.FILL,
				true, false));

		loginType.setLabelProvider(new LabelProvider(){
			@Override
			public String getText(Object element) {
				LoginType lt = (LoginType) element;
				return lt.getLoginType();
			}
		});
		loginType.setContentProvider(new ArrayContentProvider());

		//register login handler for different login types.
		LoginType shop = new LoginType(Messages.LOGIN_DIALOG_LOGIN_TYPE_SHOP);
		LoginDelegator.getInstance().registerLoginHandler(shop, new LocalLoginHandler());
		LoginType agent = new LoginType(Messages.LOGIN_DIALOG_LOGIN_TYPE_AGENT);
		LoginDelegator.getInstance().registerLoginHandler(agent, new RemoteLoginHandler());

		loginType.setInput(LoginDelegator.getInstance().getLoginTypes());
		loginType.setComparer(new IElementComparer() {

			@Override
			public boolean equals(Object a, Object b) {
				if(!(a instanceof LoginType) || !(b instanceof LoginType)){
					return false;
				}
				LoginType aType = (LoginType) a;
				LoginType bType = (LoginType) b;

				String aTypeCode = aType.getLoginType();
				String bTypeCode = bType.getLoginType();

				if(aTypeCode.equalsIgnoreCase(bTypeCode)){
					return true;
				}

				return false;
			}

			@Override
			public int hashCode(Object element) {
				return element.hashCode();
			}
		});

		String lastUser = "none"; //$NON-NLS-1$
		if (loginContext != null && loginContext.getUser() != null
				&& loginContext.getUser().getStaffInfo() != null){
			lastUser = loginContext.getUser().getStaffInfo().getStaffId();
		}
		initializeUsers(lastUser);

		return composite;
	}

	protected void createButtonsForButtonBar(Composite parent) {


		removeCurrentUser = createButton(parent,
				IDialogConstants.CLIENT_ID, Messages.LOGIN_DIALOG_DELETE_USER, false);
		removeCurrentUser.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				savedDetails.remove(userIdText.getText());
				initializeUsers(""); //$NON-NLS-1$
			}
		});
		createButton(parent, IDialogConstants.OK_ID, Messages.LOGIN_DIALOG_LOGIN, true);
		createButton(parent, IDialogConstants.CANCEL_ID,
				IDialogConstants.CANCEL_LABEL, false);

	}

	protected void initializeUsers(String defaultUser) {
		userIdText.removeAll();
		passwordText.setText(""); //$NON-NLS-1$
		for (Iterator<String> it = ((TreeMap<String, LoginContext>)savedDetails).descendingMap().keySet().iterator(); it.hasNext();)
			userIdText.add(it.next());
		int index = Math.max(userIdText.indexOf(defaultUser), 0);
		userIdText.select(index);
	}

	private void controlLoginInputs(boolean enabled){
		userIdText.setEnabled(enabled);
		passwordText.setEnabled(enabled);
		loginType.getCombo().setEnabled(enabled);
		getButton(IDialogConstants.OK_ID).setEnabled(enabled);
		removeCurrentUser.setEnabled(enabled);
	}

	protected void okPressed() {
		userIdText.setBackground(null);
		passwordText.setBackground(null);
		userIdText.setToolTipText(""); //$NON-NLS-1$
		passwordText.setToolTipText(""); //$NON-NLS-1$
		controlLoginInputs(false);

		if (StringUtils.isEmpty(userIdText.getText())) {
			MessageDialog.openError(getShell(), Messages.LOGIN_DIALOG_INVALID_USER_NAME,
					Messages.LOGIN_DIALOG_INVALID_USER_NAME_DESC);
			return;
		}

		LoginType lt = (LoginType) ((StructuredSelection)loginType.getSelection()).getFirstElement();
		if (lt == null) {
			MessageDialog.openError(getShell(), Messages.LOGIN_DIALOG_INVALID_LOGIN_TYPE,
					Messages.LOGIN_DIALOG_INVALID_LOGIN_TYPE_DESC);
			return;
		}

		RetailLoginUser user = null;
		try{
			user = LoginDelegator.getInstance().login(loginContext);
			if(user != null){
				SessionManager.getInstance().setLoginUser(user);
				super.okPressed();
			}else{
				userIdText.setBackground(SWTResourceManager.getColor(255, 128, 64));
				passwordText.setBackground(SWTResourceManager.getColor(255, 128, 64));
				userIdText.setToolTipText(Messages.LOGIN_DIALOG_WRONG_NAME_PASSWORD);
				passwordText.setToolTipText(Messages.LOGIN_DIALOG_WRONG_NAME_PASSWORD);
				controlLoginInputs(true);
			}
		}catch(RemoteConnectFailureException e){
			MessageDialog.openError(this.getParentShell(), "Login Error", "Server is not available.");
			controlLoginInputs(true);
		}catch(Exception e){
			e.printStackTrace();
			MessageDialog.openError(this.getParentShell(), "Login Error", e.getMessage());
			controlLoginInputs(true);
		}
	}

	protected void buttonPressed(int buttonId) {
		if (buttonId == IDialogConstants.CLIENT_ID) {
			return;
		}
		String userId = userIdText.getText();
		String password = passwordText.getText();
		StaffInfoTO user = new StaffInfoTO();
		user.setStaffId(userId);
		LoginUserTO loginUserTO = new LoginUserTO();
		loginUserTO.setStaffInfo(user);
		loginUserTO.setPassword(password);

		LoginType lt = (LoginType) ((StructuredSelection)loginType.getSelection()).getFirstElement();
		loginContext = new LoginContext(loginUserTO, lt);
		//if existing user exist, remove it and add it again to the tail.
		savedDetails.remove(userId);
		savedDetails.put(userId, loginContext);
		if (buttonId == IDialogConstants.OK_ID
				|| buttonId == IDialogConstants.CANCEL_ID)
			saveDescriptors();
		super.buttonPressed(buttonId);
	}

	public void saveDescriptors() {
		Preferences preferences = new ConfigurationScope()
				.getNode(Activator.PLUGIN_ID);
		preferences.put(LAST_USER, loginContext.getUser().getStaffInfo().getStaffId());
		Preferences loginSets = preferences.node(SAVED);
		for (Iterator<String> it = savedDetails.keySet().iterator(); it.hasNext();) {
			String name = it.next();
			LoginContext d = (LoginContext) savedDetails.get(name);
			Preferences loginSet = loginSets.node(name);
			if(d.getLoginType() != null){
				loginSet.put(LOGIN_TYPE, d.getLoginType().getLoginType());
			}
			loginSet.put(PASSWORD, d.getUser().getPassword());
		}
		try {
			preferences.flush();
		} catch (BackingStoreException e) {
			e.printStackTrace();
		}

	}

	private void loadDescriptors() {
		try {
			Preferences preferences = new ConfigurationScope()
					.getNode(Activator.PLUGIN_ID);
			Preferences loginSets = preferences.node(SAVED);
			String[] userIds = loginSets.childrenNames();
			for (int i = 0; i < userIds.length; i++) {
				String userId = userIds[i];
				Preferences node = loginSets.node(userId);
				StaffInfoTO user = new StaffInfoTO();
				user.setStaffId(userId);
				LoginUserTO loginUserTO = new LoginUserTO();
				loginUserTO.setStaffInfo(user);
				loginUserTO.setPassword(node
						.get(PASSWORD, "")); //$NON-NLS-1$
				savedDetails.put(userId, new LoginContext(loginUserTO, new LoginType(node.get(LOGIN_TYPE, "")))); //$NON-NLS-1$
			}
			loginContext = (LoginContext) savedDetails
					.get(preferences.get(LAST_USER, "")); //$NON-NLS-1$
		} catch (BackingStoreException e) {
			e.printStackTrace();
		}
	}

	public LoginContext getLoginContext(){
		return loginContext;
	}
}
